package self.cloud.domain.network;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.net.*;
import java.util.Enumeration;

@Component
public class NetworkService {
    private static final Logger logger = LoggerFactory.getLogger(NetworkService.class);

    public NetworkInterface findByName(String name) {
        logger.debug("method findByName [name]:{}", name);

        try {
            Enumeration<NetworkInterface> networks = NetworkInterface.getNetworkInterfaces();

            while (networks.hasMoreElements()) {
                NetworkInterface network = networks.nextElement();

                if (network.getName().equals(name)) {
                    logger.debug("found network [name]:{}", name);

                    if (network.isVirtual()) {
                        logger.warn("[network.name]:{} is Virtual Network", name);
                        continue;
                    }

                    if (network.isLoopback()) {
                        logger.warn("[network.name]:{} is Loopback Network", name);
                        continue;
                    }

                    return network;
                }
            }
        } catch (SocketException e) {
            logger.error(e.toString(), e);
        }

        return null;
    }

    public Inet4Address findByIPv4(NetworkInterface network) {
        logger.debug("method findByIPv4 [network]:{}", network.getName());

        Enumeration<InetAddress> addresses = network.getInetAddresses();

        while (addresses.hasMoreElements()) {
            InetAddress address = addresses.nextElement();

            if (address.isLinkLocalAddress()) {
                continue;
            }

            if (address instanceof Inet4Address) {
                return (Inet4Address) address;
            }
        }

        return null;
    }

    public Inet6Address findByIPv6(NetworkInterface network) {
        logger.debug("method findByIPv4 [network]:{}", network.getName());

        Enumeration<InetAddress> addresses = network.getInetAddresses();

        while (addresses.hasMoreElements()) {
            InetAddress address = addresses.nextElement();

            if (address.isLinkLocalAddress()) {
                continue;
            }

            if (address instanceof Inet6Address) {
                return (Inet6Address) address;
            }
        }

        return null;
    }


    public String findAllToString() throws SocketException {
        StringBuilder stringBuilder = new StringBuilder();
        logger.debug("method findAllToString");

        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        logger.debug("[networks.count]:{}", networkInterfaces);

        int network_count = 0;
        int effective_network_count = 0;
        while (networkInterfaces.hasMoreElements()) {
            NetworkInterface network = networkInterfaces.nextElement();

            if (network.isVirtual()) {
                continue;
            }
            if (network.isLoopback()) {
                continue;
            }

            Enumeration<InetAddress> InetAddresses = network.getInetAddresses();
            while (InetAddresses.hasMoreElements()) {
                InetAddress address = InetAddresses.nextElement();

                logger.debug("[AnyLocalAddress]:{}", address.isAnyLocalAddress());
                logger.debug("[LinkLocalAddress]:{}", address.isLinkLocalAddress());
                logger.debug("[LoopbackAddress]:{}", address.isLoopbackAddress());
                logger.debug("[MulticastAddress]:{}", address.isMulticastAddress());
                logger.debug("[SiteLocalAddress]:{}", address.isSiteLocalAddress());
                logger.debug("[MCGlobal]:{}", address.isMCGlobal());
                logger.debug("[MCLinkLocal]:{}", address.isMCLinkLocal());
                logger.debug("[MCNodeLocal]:{}", address.isMCNodeLocal());
                logger.debug("[MCOrgLocal]:{}", address.isMCOrgLocal());
                logger.debug("[MCSiteLocal]:{}", address.isMCSiteLocal());

                if (address.isLinkLocalAddress()) {
                    logger.debug("[network]:{} [index]:{} [address]:{} isLinkLocalAddress", network.getName(), network.getIndex(), address.getHostAddress());
                    continue;
                }

                if(address instanceof Inet4Address) {
                    logger.debug("[network][{}]:{} [address][ipv4]:{}", network.getIndex(), network.getName(), address.getHostAddress());
                    stringBuilder.append("[network]").append(network.getIndex()).append(':').append(network.getName())
                            .append(" [address]").append("[ipv4]").append(':').append(address.getHostAddress())
                            .append(System.getProperty("line.separator"));
                }
                else if(address instanceof Inet6Address) {
                    Inet6Address inet6Address = (Inet6Address) address;
                    logger.debug("[IPv4Compatible]:{}", inet6Address.isIPv4CompatibleAddress());
                    logger.debug("[network][{}]:{} [address][ipv6]:{}", network.getIndex(), network.getName(), address.getHostAddress());
                    stringBuilder.append("[network]").append(network.getIndex()).append(':').append(network.getName())
                            .append(" [address]").append("[ipv6]").append(':').append(address.getHostAddress())
                            .append(System.getProperty("line.separator"));
                }
                else {
                    throw new UnknownError("Unknown Address Type");
                }
                effective_network_count++;
            }
            network_count++;
        }

        stringBuilder.append("[networks.count]:").append(network_count).append(" [effective.count]:").append(effective_network_count).append(System.getProperty("line.separator"));


        return stringBuilder.toString();
    }
}
